import java.io.*;
import java.util.*;

public class TargetExecutor {
    private final Jakespec jakespec;
    private final Set<String> executedTargets;
    // 新增：用于存储环境变量（跨命令共享）
    private final Map<String, String> environmentVariables = new HashMap<>();

    public TargetExecutor(Jakespec jakespec) {
        this.jakespec = jakespec;
        this.executedTargets = new HashSet<>();
         // 初始化环境变量（继承系统环境变量）
        environmentVariables.putAll(System.getenv());
    }

    public void executeTarget(String targetName) throws IOException, InterruptedException {
        if (!jakespec.hasTarget(targetName)) {
            throw new IllegalArgumentException("目标不存在: " + targetName);
        }

        if (executedTargets.contains(targetName)) {
            return;
        }

        Target target = jakespec.getTarget(targetName);
        System.out.println("📌 开始执行目标：" + targetName);

        for (String dependency : target.getDependencies()) {
            String cleanDep = dependency.trim().replaceAll("^:", "");
            System.out.println("  → 依赖目标：" + cleanDep);
            executeTarget(cleanDep);
        }

        String workingDir = target.getDirectory() != null ?
                target.getDirectory() : System.getProperty("user.dir");
        File workingDirFile = new File(workingDir);
        if (!workingDirFile.exists()) {
            workingDirFile.mkdirs();
            System.out.println("  → 自动创建工作目录：" + workingDirFile.getAbsolutePath());
        }

        processJavaCommands(target, workingDir);

        processFatJar(target, workingDir);

        for (String command : target.getCommands()) {
            executeCommandViaShell(command, workingDir);
        }

        executedTargets.add(targetName);
        System.out.println("✅ 目标执行完成：" + targetName + "\n");
    }

 /**
     * 改进的命令执行方法，支持：
     * 1. @command：静默执行（不打印命令本身）
     * 2. -command：忽略错误（命令失败不终止构建）
     * 3. export VAR=value：设置环境变量
     * 4. $VAR：引用环境变量
     */
    private void executeCommandViaShell(String command, String workingDir) throws IOException, InterruptedException {
        // 解析命令前缀
        boolean silent = false;      // @前缀标记
        boolean ignoreError = false; // -前缀标记
        
        // 处理@前缀（静默执行）
        if (command.startsWith("@")) {
            silent = true;
            command = command.substring(1).trim();
        }
        
        // 处理-前缀（忽略错误）
        if (command.startsWith("-")) {
            ignoreError = true;
            command = command.substring(1).trim();
        }
        
        // 处理空命令（去除前缀后可能为空）
        if (command.isEmpty()) {
            return;
        }
        
        // 处理环境变量设置（export VAR=value）
        if (command.startsWith("export ")) {
            String varPart = command.substring("export ".length()).trim();
            int eqIndex = varPart.indexOf('=');
            if (eqIndex > 0 && eqIndex < varPart.length() - 1) {
                String varName = varPart.substring(0, eqIndex).trim();
                String varValue = varPart.substring(eqIndex + 1).trim();
                // 存储环境变量（后续命令可引用）
                environmentVariables.put(varName, varValue);
                if (!silent) {
                    System.out.println("设置环境变量: " + varName + "=" + varValue);
                }
            }
            return;
        }
        
        // 替换命令中的环境变量引用（$VAR）
        String processedCommand = replaceEnvironmentVariables(command);
        
        // 不静默时打印命令
        if (!silent) {
            System.out.println("执行命令: " + processedCommand);
        }
        
        // 构建shell命令
        String[] shellCommand;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            shellCommand = new String[]{"cmd.exe", "/c", processedCommand};
        } else {
            shellCommand = new String[]{"bash", "-c", processedCommand};
        }
        
        // 构建进程并继承环境变量
        ProcessBuilder pb = new ProcessBuilder(shellCommand)
                .directory(new File(workingDir))
                .redirectErrorStream(true);
        
        // 设置环境变量
        Map<String, String> env = pb.environment();
        env.putAll(environmentVariables);
        
        // 执行命令
        Process process = pb.start();
        
        // 读取命令输出
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }
        
        // 等待命令完成并检查退出码
        int exitCode = process.waitFor();
        if (exitCode != 0 && !ignoreError) {
            throw new RuntimeException("命令执行失败，退出码: " + exitCode);
        } else if (exitCode != 0 && ignoreError) {
            System.out.println("⚠️  命令执行失败（已忽略），退出码: " + exitCode);
        }
    }

     /**
     * 替换命令中的环境变量引用（如$DAMN替换为实际值）
     */
    private String replaceEnvironmentVariables(String command) {
        // 简单的变量替换逻辑，支持$VAR格式
        StringBuilder result = new StringBuilder();
        int length = command.length();
        int i = 0;
        
        while (i < length) {
            if (command.charAt(i) == '$' && i + 1 < length) {
                int j = i + 1;
                // 收集变量名（字母、数字、下划线）
                while (j < length && (Character.isLetterOrDigit(command.charAt(j)) || command.charAt(j) == '_')) {
                    j++;
                }
                String varName = command.substring(i + 1, j);
                // 替换为环境变量值（不存在则保留原格式）
                String varValue = environmentVariables.getOrDefault(varName, "$" + varName);
                result.append(varValue);
                i = j;
            } else {
                result.append(command.charAt(i));
                i++;
            }
        }
        
        return result.toString();
    }

    private void processJavaCommands(Target target, String workingDir) throws IOException, InterruptedException {
        String classpath = target.getClasspath() != null ? target.getClasspath() : jakespec.getClasspath();
        classpath = resolveClasspath(classpath, workingDir);
        
        String sourceDir = target.getSourceDir() != null ? target.getSourceDir() : jakespec.getSourceDir();
        sourceDir = (sourceDir == null) ? "src" : sourceDir;
        
        String outputDir = target.getOutputDir() != null ? target.getOutputDir() : jakespec.getOutputDir();
        outputDir = (outputDir == null) ? "bin" : outputDir;
        
        File outputDirFile = new File(workingDir, outputDir);
        if (!outputDirFile.exists()) {
            outputDirFile.mkdirs();
        }
        
        List<File> javaFiles = new ArrayList<>();
        File sourceDirFile = new File(workingDir, sourceDir);
        findJavaFiles(sourceDirFile, javaFiles);
        
        if (javaFiles.isEmpty()) {
            System.out.println("  → 未找到Java源码文件（目录：" + sourceDirFile.getAbsolutePath() + "）");
            return;
        }
        
        List<String> javacCommand = new ArrayList<>();
        javacCommand.add("javac");
        
        List<String> allCompilerArgs = new ArrayList<>();
        
        String globalJavacArgs = jakespec.getGlobalConfig("javacargs");
        if (globalJavacArgs != null && !globalJavacArgs.isEmpty()) {
            allCompilerArgs.addAll(parseCompilerArgs(globalJavacArgs));
        }
        
        allCompilerArgs.addAll(target.getCompilerArgs());
        
        for (String arg : allCompilerArgs) {
            String[] parts = arg.split("(?<!\\\\)\\s+");
            for (String part : parts) {
                if (!part.trim().isEmpty()) {
                    String cleaned = part.replace("\\ ", " ");
                    javacCommand.add(cleaned);
                }
            }
        }
        
        if (classpath != null && !classpath.isEmpty()) {
            javacCommand.add("-classpath");
            javacCommand.add(classpath);
        }
        
        javacCommand.add("-d");
        javacCommand.add(outputDirFile.getAbsolutePath());
        
        for (File file : javaFiles) {
            javacCommand.add(file.getAbsolutePath());
        }
        
        System.out.println("  → 开始编译Java代码...");
        executeCommand(javacCommand, workingDir);
    }

    private List<String> parseCompilerArgs(String argsStr) {
        List<String> args = new ArrayList<>();
        if (argsStr == null || argsStr.trim().isEmpty()) {
            return args;
        }
        
        String[] parts = argsStr.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
        for (String part : parts) {
            String trimmed = part.trim();
            if (!trimmed.isEmpty()) {
                args.add(trimmed);
            }
        }
        return args;
    }

    private void processFatJar(Target target, String workingDir) throws IOException {
        Map<String, String> props = target.getProperties();
        String outputPath = props.get("fatjar");
        String mainClass = props.get("mainclass");
        
        if (outputPath == null || mainClass == null) {
            return;
        }
        
        List<File> classDirs = new ArrayList<>();
        classDirs.add(new File(workingDir, target.getOutputDir() != null ? 
                      target.getOutputDir() : "bin"));
        
        List<File> jars = new ArrayList<>();
        File libDir = new File(workingDir, "lib");
        if (libDir.exists() && libDir.isDirectory()) {
            for (File file : libDir.listFiles()) {
                if (file.getName().endsWith(".jar")) {
                    jars.add(file);
                }
            }
        }
        
        System.out.println("  → 开始构建FatJAR...");
        new UniversalFatJarBuilder().build(outputPath, mainClass, classDirs, jars);
    }

    private String resolveClasspath(String classpath, String workingDir) {
        if (classpath == null || classpath.isEmpty()) {
            classpath = "lib/*:src:bin";
        }
        
        String separator = File.pathSeparator;
        
        if (classpath.contains("lib/*")) {
            File libDir = new File(workingDir, "lib");
            if (!libDir.exists()) {
                libDir.mkdirs();
            }
            
            File[] jarFiles = libDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".jar"));
            if (jarFiles != null && jarFiles.length > 0) {
                StringBuilder jarPath = new StringBuilder();
                for (File jar : jarFiles) {
                    if (jarPath.length() > 0) {
                        jarPath.append(separator);
                    }
                    jarPath.append(jar.getAbsolutePath());
                }
                
                classpath = classpath.replace("lib/*", jarPath.toString());
            }
        }
        
        return classpath;
    }

    private void findJavaFiles(File directory, List<File> javaFiles) {
        if (directory == null || !directory.exists() || !directory.isDirectory()) {
            return;
        }
        
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    findJavaFiles(file, javaFiles);
                } else if (file.getName().endsWith(".java")) {
                    javaFiles.add(file);
                }
            }
        }
    }

    private void executeCommand(String command, String workingDir) throws IOException, InterruptedException {
        System.out.println("执行命令: " + command);
        
        List<String> commandParts = splitCommand(command);
        ProcessBuilder pb = new ProcessBuilder(commandParts)
                .directory(new File(workingDir))
                .inheritIO();
        
        Process process = pb.start();
        int exitCode = process.waitFor();
        
        if (exitCode != 0) {
            throw new RuntimeException("命令执行失败，退出码: " + exitCode);
        }
    }

    private void executeCommand(List<String> command, String workingDir) throws IOException, InterruptedException {
        System.out.println("执行命令: " + String.join(" ", command));
        
        ProcessBuilder pb = new ProcessBuilder(command)
                .directory(new File(workingDir))
                .inheritIO();
        
        Process process = pb.start();
        int exitCode = process.waitFor();
        
        if (exitCode != 0) {
            throw new RuntimeException("命令执行失败，退出码: " + exitCode);
        }
    }

    private List<String> splitCommand(String command) {
        List<String> parts = new ArrayList<>();
        StringBuilder currentPart = new StringBuilder();
        boolean inQuotes = false;
        char quoteChar = ' ';
        
        for (char c : command.toCharArray()) {
            if ((c == '"' || c == '\'') && (quoteChar == ' ' || c == quoteChar)) {
                inQuotes = !inQuotes;
                if (quoteChar == ' ') {
                    quoteChar = c;
                } else if (c == quoteChar) {
                    quoteChar = ' ';
                }
                currentPart.append(c);
            } else if (Character.isWhitespace(c) && !inQuotes) {
                if (currentPart.length() > 0) {
                    parts.add(currentPart.toString());
                    currentPart.setLength(0);
                }
            } else {
                currentPart.append(c);
            }
        }
        
        if (currentPart.length() > 0) {
            parts.add(currentPart.toString());
        }
        
        for (int i = 0; i < parts.size(); i++) {
            String part = parts.get(i);
            if ((part.startsWith("\"") && part.endsWith("\"")) || 
                (part.startsWith("'") && part.endsWith("'"))) {
                parts.set(i, part.substring(1, part.length() - 1));
            }
        }
        
        return parts;
    }
}